/******************************************************************/
/*                                                                */
/*  Winpooch : Windows Watchdog                                   */
/*  Copyright (C) 2004-2005  Benoit Blanchon                      */
/*                                                                */
/*  This program is free software; you can redistribute it        */
/*  and/or modify it under the terms of the GNU General Public    */
/*  License as published by the Free Software Foundation; either  */
/*  version 2 of the License, or (at your option) any later       */
/*  version.                                                      */
/*                                                                */
/*  This program is distributed in the hope that it will be       */
/*  useful, but WITHOUT ANY WARRANTY; without even the implied    */
/*  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR       */
/*  PURPOSE.  See the GNU General Public License for more         */
/*  details.                                                      */
/*                                                                */
/*  You should have received a copy of the GNU General Public     */
/*  License along with this program; if not, write to the Free    */
/*  Software Foundation, Inc.,                                    */
/*  675 Mass Ave, Cambridge, MA 02139, USA.                       */
/*                                                                */
/******************************************************************/


/******************************************************************/
/* Build configuration                                            */
/******************************************************************/

//#define INJECT_ONLY	TEXT("cmd.exe")

#define TRACE_LEVEL	2	// warning level


/******************************************************************/
/* Includes                                                       */
/******************************************************************/

// module's interface
#include "SpyServer.h"

// Windows headers
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <psapi.h>
#include <tlhelp32.h>

// project's headers
#include "AskDlg.h"
#include "Assert.h"
#include "Config.h"
#include "EventLog.h"
#include "Filter.h"
#include "FilterFile.h"
#include "FilterSet.h"
#include "Language.h"
#include "ProcList.h"
#include "ProcListWnd.h"
#include "ProjectInfo.h"
#include "Reason.h"
#include "Resources.h"
#include "Rule.h"
#include "RuleDlg.h"
#include "Scanner.h"
#include "SpyDll.h"
#include "Strlcpy.h"
#include "TrayIcon.h"
#include "Trace.h"
#include "VirusDlg.h"


/******************************************************************/
/* Internal macros                                                */
/******************************************************************/

#define arraysize(A) (sizeof(A)/sizeof((A)[0]))


/******************************************************************/
/* Internal constants                                             */
/******************************************************************/

#define	DISCOVER_INTERVAL	5000 /*ms*/

TCHAR szFilterFilename[] = TEXT("Current.wpf") ;

TCHAR szNoFiltersAlert[] = 
  TEXT("No filter file has been found, Default filters will be used.\n"
       "You can just ignore this alert if you are running Winpooch for the first time.") ;

TCHAR szHiWatAlert[] =
  TEXT("The key pool of Winpooch is almost full.\n"
       "Winpooch will have to refuse access to registry.\n"
       "You should change your filters to watch less keys.") ;

TCHAR szLoWatAlert[] =
  TEXT("The key pool of Winpooch return to a normal level.") ;

TCHAR szFiltersErrorAlert[] =
  TEXT("Error reading file %s :\n%s\nDefault filters will be used.") ;


/******************************************************************/
/* Internal data                                                  */
/******************************************************************/

static HINSTANCE	g_hInstance = NULL ;
static HWND		g_hwndMain = NULL ;
static HFILTERSET	g_hFilterSet = NULL ;
static HANDLE		g_hFilterMutex = NULL ;
static HANDLE		g_hDiscoverMutex = NULL ;
static UINT		g_nDiscoverTimer = 0 ;
static TCHAR		g_szSpyDllPath[MAX_PATH] ;


/******************************************************************/
/* Internal functions                                             */
/******************************************************************/

VOID	_SpySrv_LockFilter () ;

VOID	_SpySrv_UnlockFilter () ;

// inject DLL into all running processes
//BOOL _SpySrv_InjectDll () ;
VOID CALLBACK _SpySrv_DiscoverTimerProc (HWND, UINT, UINT, DWORD) ;

// Filter function called by notify proc
DWORD WINAPI _SpySrv_FilterFunc (DWORD dwProcessId, CONDITION * pCond) ;

// Notification function called by spy DLL
DWORD WINAPI _SpySrv_SpyDllNotify (SDNMHDR*) ;

// create initial filter set
BOOL _SpySrv_LoadDefaultFilterSet () ;

// initialize spy dll
BOOL _SpySrv_InitSpyDll (HWND hwnd) ;

// destroy filters
BOOL _SpySrv_UninitFilters () ;

// uninitialize spy dll
VOID _SpySrv_UninitSpyDll () ;

// set Debug privilege
BOOL _SpySrv_SetDebugPrivilege (BOOL) ;



/******************************************************************/
/* Internal function : NewProcessCallback                         */
/******************************************************************/

BOOL _SpySrv_NewProcessCallback (void * pContext, PROCSTRUCT * pProcess)
{
  HFILTER	hFilter ;
  BOOL		bHookEnabled ;
  HINSTANCE	hSpyDllInst ;

  TRACE ;

  // verify params
  ASSERT (pProcess!=NULL) ;

#ifdef INJECT_ONLY
  if( _tcsicmp (pProcess->szName, INJECT_ONLY) ) {
    pProcess->nState = PS_HOOK_DISABLED ;
    return TRUE ;
  }
#endif
 
  // current process
  if( pProcess->nProcessId==GetCurrentProcessId() ) {
    pProcess->nState = PS_HOOKED_SINCE_BIRTH ;
    return TRUE ;
  }  

  // acquire access to filter set
  _SpySrv_LockFilter () ;

  // get filter for this program
  hFilter = FilterSet_GetFilterStrict (g_hFilterSet, pProcess->szPath) ;

  bHookEnabled = !hFilter || Filter_IsHookEnabled(hFilter) ;

  // unlockFilter access before injecting
  _SpySrv_UnlockFilter () ;

  // is filter enabled ?
  if( bHookEnabled )
    {
      hSpyDllInst = SpyDll_IsProcessInjected (pProcess->nProcessId,
					      g_szSpyDllPath) ;
      
      // already injected ?
      if( hSpyDllInst ) 
	{
	  pProcess->nState = PS_HOOKED_BY_PREV_INSTANCE ;
	}
      // else inject now
      else 
	{  
	  if( SpyDll_InjectInProcess (pProcess->nProcessId) ) 
	    pProcess->nState = PS_HOOKED_WHILE_RUNNING ;
	  else 
	    pProcess->nState = PS_HOOK_FAILED ;	      
	}
    }
  // else filter disabled
  else
    {
      // already injected => eject now
      
      hSpyDllInst =  SpyDll_IsProcessInjected(pProcess->nProcessId,
					      g_szSpyDllPath) ;
      
      if( hSpyDllInst )
	SpyDll_EjectFromProcess (pProcess->nProcessId, hSpyDllInst) ;
      
      pProcess->nState = PS_HOOK_DISABLED ;
    } 

  return TRUE ;
}


/******************************************************************/
/* Internal function : InjectDll                                  */
/******************************************************************/
/*
BOOL _SpySrv_InjectDll ()
{
  TRACE ;

  ProcList_Discover (_SpySrv_NewProcessCallback, NULL, NULL, NULL) ;
  
  return TRUE ;
}
*/


/******************************************************************/
/* Internal function : FilterFunc                                 */
/******************************************************************/

DWORD WINAPI _SpySrv_FilterFunc (DWORD dwProcessId, CONDITION * pCond)
{
  HANDLE	hProcess ;
  TCHAR		szProcess[MAX_PATH] ;
  HFILTER	hFilter ;
  BOOL		bResult ;
  WORD		nReaction, nVerbosity, nOptions ;

  TRACE ; 

  // verify params
  ASSERT (pCond!=NULL) ;

  // get process name
  hProcess = OpenProcess (PROCESS_ALL_ACCESS, TRUE, dwProcessId) ;
  GetModuleFileNameEx (hProcess, NULL, szProcess, MAX_PATH) ;
  CloseHandle (hProcess) ;

  // acquire access to filter set
  _SpySrv_LockFilter () ;

  // try to find a filter than fit to this process name
  hFilter = FilterSet_GetFilter (g_hFilterSet, szProcess) ;
  bResult = hFilter ? Filter_Test (hFilter, pCond, &nReaction, &nVerbosity, &nOptions) : FALSE ;

  // else try with global filters
  if( ! bResult ) {
    hFilter = FilterSet_GetDefaultFilter (g_hFilterSet) ;
    bResult = hFilter ? Filter_Test (hFilter,pCond,&nReaction,&nVerbosity, &nOptions) : FALSE ;
  }

  // release acces to filter set asap
  _SpySrv_UnlockFilter () ;

  // default flags
  if( ! bResult )  
    {
      nReaction  = RULE_ACCEPT ;
      nVerbosity = RULE_SILENT ;
      nOptions = 0 ;
    }
    
  if( nOptions & RULE_ASK )
    {
      ASKDLGPARAMS params = {
	szProcess,
	dwProcessId,
	nReaction,
	pCond,
      } ;
      int nResult ;

    askdlg:
      
      nResult = DialogBoxParam (g_hInstance, MAKEINTRESOURCE(DLG_ASK), g_hwndMain, 
				AskDlg_DlgProc, (LPARAM)&params) ;

      if( nResult==ASKDLG_NEW_FILTER )
	{
	  RULE	* pRule ;

	  pRule = (RULE*) malloc (sizeof(RULE)) ;
	  pRule->nReaction	= nReaction ;
	  pRule->nVerbosity	= RULE_LOG ;
	  pRule->nOptions	= 0 ;
	  pRule->pCondition	= Condition_Dup (pCond) ;

	  if( IDOK!=DialogBoxParam (g_hInstance, MAKEINTRESOURCE(DLG_RULE), 
				    g_hwndMain, RuleDlg_DlgProc, (LPARAM)pRule) )
	    {
	      free (pRule->pCondition) ;
	      free (pRule) ;
	      goto askdlg ;
	    }

	  if( ! Condition_Check(pRule->pCondition,pCond) )
	    {
	      MessageBox (g_hwndMain, 
			  STR_DEF(_RULE_DOESNT_MATCH,
				  TEXT("The rule you defined doesn't match current event")), 
			  TEXT(APPLICATION_NAME), MB_ICONERROR) ;
	      free (pRule->pCondition) ;
	      free (pRule) ;
	      goto askdlg ;	      
	    }

	  _SpySrv_LockFilter () ;
	  hFilter = FilterSet_GetFilterStrict (g_hFilterSet, szProcess) ;
	  if( ! hFilter ) {
	    hFilter = Filter_Create (szProcess) ;
	    FilterSet_AddFilter (g_hFilterSet, hFilter) ;
	  }
	  Filter_AddRule (hFilter, pRule) ;
	  _SpySrv_UnlockFilter () ;
	  PostMessage (g_hwndMain, WM_SPYNOTIFY, SN_FILTERCHANGED, 0) ;

	  nReaction = pRule->nReaction ;
	  nVerbosity = pRule->nVerbosity ;
	  nOptions = pRule->nOptions ;
	}
      else
	{
	  nReaction = nResult ;
	  nVerbosity = RULE_LOG ;
	  nOptions = 0 ;
	}
    }

  if( nOptions & RULE_SCAN )
    {
      TCHAR szOutput[1024] ;
      UINT nResult ;
      HCURSOR hPrevCurs ;

      ASSERT (Condition_GetParamType(pCond,0)==PARAM_STRING) ;

      hPrevCurs = SetCursor (LoadCursor(NULL,IDC_WAIT)) ;
      nResult = Scanner_ScanFile (Condition_GetParamString(pCond,0),szOutput,1024) ;
      SetCursor (hPrevCurs) ;
     
      if( nResult!=SCAN_NO_VIRUS )
	{
	  VIRUSDLGPARAMS params = {
	    Condition_GetParamString(pCond,0),
	    szOutput
	  } ;
	  
	  nReaction = DialogBoxParam (g_hInstance, MAKEINTRESOURCE(DLG_VIRUS), g_hwndMain, 
				      VirusDlg_DlgProc, (LPARAM)&params) ;
	  
	} 
/*      else if( nResult==SCAN_FAILED )
	{
	  if( IDYES==MessageBox (g_hwndMain,
				 TEXT("The anti-virus failed to scan this file.\n"
				      "Do you want to reject ?"), TEXT("Winpooch"),
				 MB_YESNO|MB_ICONWARNING) )
	    nResult = RULE_REJECT ;
	}
 */
    }
  
  if( nVerbosity==RULE_LOG || nVerbosity==RULE_ALERT )
    {
      DWORD dwEventId ;

      // get process base name
      hProcess = OpenProcess (PROCESS_ALL_ACCESS, TRUE, dwProcessId) ;
      GetModuleBaseName (hProcess, NULL, szProcess, 32) ; 
      CloseHandle (hProcess) ;

      dwEventId = EventLog_Add (dwProcessId, szProcess, 
				nReaction, nVerbosity, pCond) ;

      if( dwEventId==0xFFFFFFFF )
	TRACE_ERROR (TEXT("EventLog_Add failed\n")) ;

      if( nVerbosity==RULE_ALERT )
	PostMessage (g_hwndMain, WM_SPYNOTIFY, SN_ALERT, dwEventId) ;

      PostMessage (g_hwndMain, WM_SPYNOTIFY, SN_EVENTLOGGED, dwEventId) ;
    }
   
  return nReaction ;
}


/******************************************************************/
/* Internal function : SpyDllNotify                               */
/******************************************************************/

DWORD WINAPI _SpySrv_SpyDllNotify (SDNMHDR * p) 
{
  DWORD	dwResult = 0 ;

  TRACE ; 

  switch( p->dwCode )
    {
    case SDN_CATCH_CALL:
      {
	SDNCATCHCALL	*pCatchCall = (SDNCATCHCALL*)p ;
	dwResult = _SpySrv_FilterFunc (p->dwProcessId, &pCatchCall->cond) ;
      }
      break ;

    case SDN_CREATING_PROCESS:
      {
	HFILTER			hFilter ;
	SDNCREATINGPROCESS	*pCreatingProcess ;
	
	pCreatingProcess = (SDNCREATINGPROCESS*) p ;
	TRACE_INFO (TEXT("Creating process %s\n"), pCreatingProcess->szPath) ;

	// disable process discovery
	WaitForSingleObject (g_hDiscoverMutex, INFINITE) ;

	// is it antivirus ?
	if( Scanner_IsScanner(pCreatingProcess->szPath) )
	  {
	    // don't hook antivirus
	    dwResult = FALSE ;
	  }
	// else look in filters
	else 
	  {
	    _SpySrv_LockFilter () ;	  
	    hFilter = FilterSet_GetFilter (g_hFilterSet, pCreatingProcess->szPath) ;	  
	    dwResult = !hFilter || Filter_IsHookEnabled(hFilter) ;
	    _SpySrv_UnlockFilter () ;
	  }
      }
      break ;

    case SDN_PROCESS_CREATED:
      {
	PROCSTRUCT		ps ;
	SDNPROCESSCREATED	*pProcessCreated ;
	LPTSTR			pBaseName ;

	pProcessCreated = (SDNPROCESSCREATED*) p ;
	
	TRACE_INFO (TEXT("Process created %s\n"), pProcessCreated->szPath) ;
	
	ps.nProcessId = pProcessCreated->dwProcessId ;
	ps.nState = pProcessCreated->bHooked ? PS_HOOKED_SINCE_BIRTH : PS_HOOK_DISABLED ;
	_tcscpy (ps.szPath, pProcessCreated->szPath) ;
	pBaseName = _tcsrchr (ps.szPath, TEXT('\\')) ;
	if( pBaseName ) pBaseName++ ;
	else pBaseName = pProcessCreated->szPath ;
	_tcslcpy (ps.szName, pBaseName, arraysize(ps.szName)) ;
	
	ProcList_Add (&ps) ;

	// enable process discovery
	ReleaseMutex (g_hDiscoverMutex) ;
	
	PostMessage (g_hwndMain, WM_SPYNOTIFY, SN_PROCESSCHANGED, ps.nProcessId) ;
      }      
      break ;

    case SDN_CREATING_FAILED:
      {
	// disable process discovery
	ReleaseMutex (g_hDiscoverMutex) ;
      }
      break ;

    case SDN_NEW_REGISTRY_KEY:
      {
	SDNNEWREGKEY	*pNewRegKey = (SDNNEWREGKEY*)p  ;

	_SpySrv_LockFilter () ;
	
	dwResult = FilterSet_TestPartialKey (g_hFilterSet, pNewRegKey->szKey) ;
	
	_SpySrv_UnlockFilter () ;
      }
      break ;

    case SDN_KEY_POOL_HIWAT:
      TrayIcon_Alert (STR_DEF(_KEY_POOL_HIWAT, szHiWatAlert)) ;      
      break ;
      
    case SDN_KEY_POOL_LOWAT:
      TrayIcon_Alert (STR_DEF(_KEY_POOL_LOWAT, szLoWatAlert)) ;      
      break ;

    default:

      TRACE_WARNING (TEXT("Spy notification not handled (code=%d)\n"),  p->dwCode) ;
    }

  return dwResult ;
}


/******************************************************************/
/* Exported function : GetFilterSet                               */
/******************************************************************/

HFILTERSET SpySrv_GetFilterSet () 
{
  TRACE ; 

  return g_hFilterSet ;
}

/******************************************************************/
/* Exported function : SetFilterSet                               */
/******************************************************************/
/*
VOID SpySrv_SetFilterSet (HFILTERSET hNewFilterSet) 
{
  TRACE ; 

  ASSERT (hNewFilterSet!=NULL) ;

  _SpySrv_LockFilter () ;
  
  FilterSet_Destroy (g_hFilterSet) ;
  g_hFilterSet = hNewFilterSet ;

  _SpySrv_UnlockFilter () ;
}
*/


/******************************************************************/
/* Internal function : LoadDefaultFilterSet                       */
/******************************************************************/

BOOL _SpySrv_LoadDefaultFilterSet ()
{
  HFILTERSET	hCurFilter ;
  CONDITION	*pCondition ;

  TRACE ; 

  g_hFilterSet = FilterSet_Create (64) ;
  
  hCurFilter = Filter_Create (TEXT("*")) ;

  Filter_AddNewRule (hCurFilter, RULE_ACCEPT, RULE_SILENT, 0,
  		     Condition_AllocAndSet(REASON_NET_LISTEN, TEXT("s*"), TEXT("127.0.0.1"))) ;

  Filter_AddNewRule (hCurFilter, RULE_ACCEPT, RULE_ALERT, 0,
  		     Condition_AllocAndSet(REASON_NET_LISTEN, TEXT("**"))) ;

  Filter_AddNewRule (hCurFilter, RULE_ACCEPT, RULE_SILENT, 0,
  		     Condition_AllocAndSet(REASON_NET_CONNECT, TEXT("s*"), TEXT("127.0.0.1"))) ;

  Filter_AddNewRule (hCurFilter, RULE_ACCEPT, RULE_SILENT, 0, 
  		     Condition_AllocAndSet(REASON_NET_CONNECT, TEXT("*n"), 443)) ;

  Filter_AddNewRule (hCurFilter, RULE_ACCEPT, RULE_SILENT, 0,
  		     Condition_AllocAndSet(REASON_NET_CONNECT, TEXT("*n"), 80)) ;

  Filter_AddNewRule (hCurFilter, RULE_ACCEPT, RULE_LOG, 0, 
  		     Condition_AllocAndSet(REASON_NET_CONNECT, TEXT("**"))) ;

  Filter_AddNewRule (hCurFilter, RULE_ACCEPT, RULE_LOG, RULE_SCAN,
		     Condition_AllocAndSet(REASON_SYS_EXECUTE, TEXT("*"))) ;

  pCondition = Condition_AllocAndSet(REASON_FILE_WRITE, TEXT("w"), TEXT("?:\\Windows\\*.dll")) ;
  Filter_AddNewRule (hCurFilter, RULE_REJECT, RULE_LOG, RULE_ASK, pCondition) ;

  pCondition = Condition_AllocAndSet(REASON_FILE_WRITE, TEXT("w"), TEXT("?:\\Windows\\*.exe")) ;
  Filter_AddNewRule (hCurFilter, RULE_REJECT, RULE_LOG, RULE_ASK, pCondition) ; 

  pCondition = Condition_AllocAndSet(REASON_FILE_WRITE, TEXT("w"), TEXT("?:\\Winnt\\*.dll")) ;
  Filter_AddNewRule (hCurFilter, RULE_REJECT, RULE_LOG, RULE_ASK, pCondition) ;

  pCondition = Condition_AllocAndSet(REASON_FILE_WRITE, TEXT("w"), TEXT("?:\\Winnt\\*.exe")) ;
  Filter_AddNewRule (hCurFilter, RULE_REJECT, RULE_LOG, RULE_ASK, pCondition) ; 

  pCondition = Condition_AllocAndSet (REASON_REG_SETVALUE, TEXT("w*"), 
				      TEXT("*\\Software\\Microsoft\\Windows\\CurrentVersion\\Run*")) ;
  Filter_AddNewRule (hCurFilter, RULE_REJECT, RULE_LOG, RULE_ASK, pCondition) ;	

  FilterSet_AddFilter (g_hFilterSet, hCurFilter) ;
    
  return TRUE ;
}


/******************************************************************/
/* Internal function : InitSpyDll                                 */
/******************************************************************/

BOOL _SpySrv_InitSpyDll (HWND hwnd)
{
  TRACE ;

  SpyDll_Init (GetCurrentProcessId(), _SpySrv_SpyDllNotify) ;

  return TRUE ;
}


/******************************************************************/
/* Internal function : SetDebugPrivilege                          */
/******************************************************************/

BOOL _SpySrv_SetDebugPrivilege (BOOL bEnabled)
{
  HANDLE		hToken ;
  BOOL			bSuccess ;
  LUID			luid ;
  TOKEN_PRIVILEGES	tp ;
  
  TRACE ; 

  // open token handle
  bSuccess = OpenProcessToken (GetCurrentProcess(),
			       TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
			       &hToken) ;
  if( ! bSuccess ) {
    TRACE_ERROR ("OpenProcessToken failed") ;
    return FALSE ;
  }

  // get SeDebugName LUID
  bSuccess = LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &luid) ;
  if( ! bSuccess ) {
    TRACE_ERROR ("LookupPrivilegeValue failed") ;
    CloseHandle (hToken) ;
    return FALSE ;
  }
  
  // fill TOKEN_PRIVILEGES
  tp.PrivilegeCount		= 1 ;
  tp.Privileges[0].Luid		= luid ;
  tp.Privileges[0].Attributes	= bEnabled ? SE_PRIVILEGE_ENABLED : 0 ;

  // set privilege
  bSuccess = AdjustTokenPrivileges (hToken, FALSE, &tp, sizeof(tp), NULL, NULL) ;
  if( ! bSuccess ) {
    TRACE_ERROR ("AdjustTokenPrivileges failed") ;
    CloseHandle (hToken) ;
    return FALSE ;
  }

  if (GetLastError() != ERROR_SUCCESS) {
    TRACE_ERROR ("GetLastError != ERROR_SUCCESS") ;
    CloseHandle (hToken) ;
    return FALSE ;
  }
  
  // close token handle
  CloseHandle (hToken) ;

  return TRUE ;
}


/******************************************************************/
/* Exported function : Init                                       */
/******************************************************************/

BOOL SpySrv_Init (HWND hwndMain) 
{
  HINSTANCE hSpyDllInstance ;

  TRACE ; 

  g_hwndMain = hwndMain ;
  g_hInstance = (HINSTANCE) GetWindowLong (hwndMain, GWL_HINSTANCE) ;

  g_hFilterMutex = CreateMutex (NULL, FALSE, NULL) ;

  g_hDiscoverMutex = CreateMutex (NULL, FALSE, NULL) ;
 
  _SpySrv_LoadDefaultFilterSet () ; 

  // get spy dll path
  hSpyDllInstance = GetModuleHandle (TEXT("SpyDll.dll")) ;
  ASSERT (hSpyDllInstance!=NULL) ;
  GetModuleFileName (hSpyDllInstance, g_szSpyDllPath, MAX_PATH) ;

  // initialize DLL
  if( ! _SpySrv_InitSpyDll(g_hwndMain) )
    return FALSE ;
  
  // read configuration
  SpySrv_ReloadConfig () ;

  return TRUE ;
} 


/******************************************************************/
/* Exported function : Uninit                                     */
/******************************************************************/

VOID	SpySrv_Uninit () 
{  
  TRACE ; 

  _SpySrv_UninitSpyDll () ;

  _SpySrv_UninitFilters () ;

  CloseHandle (g_hDiscoverMutex) ;
  CloseHandle (g_hFilterMutex) ;
}


/******************************************************************/
/* Exported function : Start                                      */
/******************************************************************/

BOOL SpySrv_ReloadConfig () 
{  
  TRACE ; 

  _SpySrv_SetDebugPrivilege (Config_GetInteger(CFGINT_DEBUG_PRIVILEGE)) ;
    
  return TRUE ;
}


/******************************************************************/
/* Exported function : Start                                      */
/******************************************************************/

BOOL SpySrv_Start (HWND hwndMain) 
{  
  TRACE ; 

  // inject dll now
  _SpySrv_DiscoverTimerProc (NULL, 0, 0, 0) ;

#if DISCOVER_INTERVAL
  g_nDiscoverTimer = SetTimer (NULL, 0, DISCOVER_INTERVAL, 
			       _SpySrv_DiscoverTimerProc) ;
#endif
  
  return TRUE ;
}


/******************************************************************/
/* Internal function : UninitFilters                              */
/******************************************************************/

VOID CALLBACK _SpySrv_DiscoverTimerProc (HWND hwnd, UINT nMsg, 
					 UINT idEvent, DWORD dwTime)
{
  int nChanges ;
  
  WaitForSingleObject (g_hDiscoverMutex, INFINITE) ;

  nChanges = ProcList_Discover (_SpySrv_NewProcessCallback, NULL, NULL, NULL) ;

  ReleaseMutex (g_hDiscoverMutex) ;

  if( nChanges>0 ) 
    PostMessage (g_hwndMain, WM_SPYNOTIFY, SN_PROCESSCHANGED, 0) ;
}



/******************************************************************/
/* Internal function : EjectDll                                   */
/******************************************************************/

BOOL _SpySrv_EjectDll ()
{    
  HANDLE		hSnapShot ;
  PROCESSENTRY32	pe32 ;
  HINSTANCE		hSpyDllInst ;
  
  TRACE ; 
  
  hSnapShot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0) ;
  if( hSnapShot==INVALID_HANDLE_VALUE ) return FALSE ;
  
  pe32.dwSize = sizeof(pe32) ;

  if( ! Process32First (hSnapShot, &pe32 ) )
    {
      TRACE_ERROR ("Process32First failed (error=%d)\n", GetLastError()) ;
      CloseHandle (hSnapShot) ;      
      return FALSE ;
    }  
  
  do {

    // skip process 0
    if( pe32.th32ProcessID==0 )
      continue ; 

    // skip current process
    if( pe32.th32ProcessID==GetCurrentProcessId() )
      continue ;

    hSpyDllInst = SpyDll_IsProcessInjected (pe32.th32ProcessID,
					    g_szSpyDllPath) ;
    
    if( hSpyDllInst )
      SpyDll_EjectFromProcess (pe32.th32ProcessID, hSpyDllInst) ;
  }
  while( Process32Next(hSnapShot,&pe32) ) ;
  
  CloseHandle (hSnapShot) ;
  
  TRACE ;
  
  return TRUE ;
}



/******************************************************************/
/* Exported function : Stop                                       */
/******************************************************************/

VOID SpySrv_Stop () 
{  
  TRACE ; 

  KillTimer (NULL, g_nDiscoverTimer) ;

  _SpySrv_EjectDll () ;
}


/******************************************************************/
/* Internal function : UninitFilters                              */
/******************************************************************/

BOOL _SpySrv_UninitFilters ()
{  
  TRACE ; 

  FilterSet_Destroy (g_hFilterSet) ; 

  return TRUE ;
}


/******************************************************************/
/* Internal function : UninitSpyDll                               */
/******************************************************************/

VOID _SpySrv_UninitSpyDll ()
{  
  TRACE ; 

  SpyDll_Uninit () ;
}


/******************************************************************/
/* Internal function : LockFilter                                       */
/******************************************************************/

VOID _SpySrv_LockFilter ()
{
  DWORD dwResult ;

  //  TRACE ;

  dwResult = WaitForSingleObject (g_hFilterMutex, 10000) ;

  if( dwResult!=WAIT_OBJECT_0 )
    TRACE_ERROR (TEXT("WaitForSingleObject failed (res=%u, error=%u)\n"),
		  dwResult, GetLastError()) ;
}


/******************************************************************/
/* Internal function : UnlockFilter                                     */
/******************************************************************/

VOID _SpySrv_UnlockFilter ()
{
  //TRACE ;

  if( ! ReleaseMutex (g_hFilterMutex) )
    TRACE_ERROR (TEXT("ReleaseMutex failed (error=%u)\n"), GetLastError()) ;  
}


/******************************************************************/
/* Internal function : GetAbsolutePath                            */
/******************************************************************/

void _SpySrv_GetAbsolutePath (LPTSTR szPath, LPCTSTR szFile)
{
  TCHAR *p ;
  
  GetModuleFileName (NULL, szPath, MAX_PATH) ;
  
  p = _tcsrchr (szPath, TEXT('\\')) ;  
  ASSERT (p!=NULL) ;
  _tcscpy (p+1, szFile) ;
}


/******************************************************************/
/* Exported function : ReadFilterFile                             */
/******************************************************************/  

VOID	SpySrv_ReadFilterFile ()
{
  HFILTERSET	hNewFilterSet ;
  TCHAR		szPath[MAX_PATH] ;
  TCHAR		szBuffer[1024] ;

  _SpySrv_GetAbsolutePath (szPath, szFilterFilename) ;

  if( 0xFFFFFFFF==GetFileAttributes(szPath) )
    {
      MessageBox (NULL, 
		  STR_DEF(_NO_FILTERS,szNoFiltersAlert), 
		  TEXT(APPLICATION_NAME), MB_ICONWARNING|MB_SETFOREGROUND) ;
      return ;
    }
  
  hNewFilterSet = FilterFile_Read (szPath) ;

  if( ! hNewFilterSet ) 
    {
      wsprintf (szBuffer, 
		STR_DEF(_ERROR_IN_FILTERS,szFiltersErrorAlert),
		szFilterFilename, FilterFile_GetErrorString()) ;
      MessageBox (NULL, szBuffer, TEXT(APPLICATION_NAME), MB_ICONERROR|MB_SETFOREGROUND) ;
      
      return ;
    } 


  _SpySrv_LockFilter () ;
  
  FilterSet_Destroy (g_hFilterSet) ;
  g_hFilterSet = hNewFilterSet ;
  
  _SpySrv_UnlockFilter () ; 
}


/******************************************************************/
/* Exported function : WriteFilterFile                            */
/******************************************************************/

VOID	SpySrv_WriteFilterFile ()
{
  TCHAR		szPath[MAX_PATH] ;

  _SpySrv_LockFilter () ;

  _SpySrv_GetAbsolutePath (szPath, szFilterFilename) ;
 
  FilterFile_Write (szPath, g_hFilterSet) ;
  
  _SpySrv_UnlockFilter () ; 
}
